/** @format */

import XLSX from 'xlsx'
import fs from 'fs-extra'
import { resolve, join, basename, dirname, relative } from 'path'
import { pathToFileURL } from 'url'

export async function generateIncrease(config) {
  console.log('开始生成xlsx文件...')

  const {
    i18nDir,
    xlsxDir,
    languages,
    searchLanguage = 'cn',
    modulePattern = '**/*.js',
    xlsxFileName = 'i18n_translations.xlsx',
    exportMode = 'single', // 导出模式: 'single' 或 'multiple'
    isFile = false, // 是否为单一文件模式
  } = config

  // 确保输出目录存在
  await fs.ensureDir(xlsxDir)

  if (isFile) {
    // 单一文件模式 - 直接处理指定的文件
    await generateFromFile(config)
  } else {
    // 目录模式 - 获取所有模块文件
    const moduleFiles = await getModuleFiles(
      i18nDir,
      searchLanguage,
      modulePattern
    )

    if (exportMode === 'single') {
      // 单文件模式 - 将所有模块合并到一个XLSX文件中
      await generateSingleFile(moduleFiles, config)
    } else {
      // 多文件模式 - 每个模块生成一个单独的XLSX文件
      await generateMultipleFiles(moduleFiles, config)
    }
  }

  console.log('xlsx文件生成完成!')
}

async function generateSingleFile(moduleFiles, config) {
  const {
    i18nDir,
    xlsxDir,
    languages,
    xlsxFileName,
    alias = { '@': './src' },
  } = config

  // 创建一个工作簿
  const workbook = XLSX.utils.book_new()

  const { files, isFile } = moduleFiles
  // 处理每个模块文件
  for (const moduleFile of files) {
    console.log(`处理模块: ${moduleFile}`)

    // 读取各语言的配置
    const languageData = await readLanguageData(
      i18nDir,
      languages,
      moduleFile,
      alias,
      isFile
    )

    // 为当前模块创建一个工作表并添加到工作簿
    const worksheet = await createWorksheet(
      moduleFile,
      languageData,
      languages,
      config
    )
    // 使用模块文件的相对路径作为工作表名称，将路径分隔符替换为下划线
    const sheetName = moduleFile
      .replace('.js', '')
      .replace(/[/\\]/g, '_')
      .substring(0, 31) // Excel工作表名称限制为31个字符
    XLSX.utils.book_append_sheet(workbook, worksheet, sheetName)
  }

  // 写入文件，使用可配置的文件名
  const outputPath = join(xlsxDir, xlsxFileName)
  XLSX.writeFile(workbook, outputPath)

  console.log(`生成文件: ${outputPath}`)
}

async function generateMultipleFiles(moduleFiles, config) {
  const { i18nDir, xlsxDir, languages, alias = { '@': './src' } } = config

  const { files, isFile } = moduleFiles
  // 处理每个模块文件
  for (const moduleFile of files) {
    console.log(`处理模块: ${moduleFile}`)

    // 读取各语言的配置
    const languageData = await readLanguageData(
      i18nDir,
      languages,
      moduleFile,
      alias,
      isFile
    )
    // 为当前模块创建一个XLSX文件
    const workbook = XLSX.utils.book_new()
    const worksheet = await createWorksheet(
      moduleFile,
      languageData,
      languages,
      config
    )
    XLSX.utils.book_append_sheet(workbook, worksheet, 'i18n')

    // 使用模块文件名作为XLSX文件名，将路径分隔符替换为下划线以拍平结构
    const xlsxFileName = moduleFile
      .replace('.js', '.xlsx')
      .replace(/[/\\]/g, '_')
    const outputPath = join(xlsxDir, xlsxFileName)
    XLSX.writeFile(workbook, outputPath)

    console.log(`生成文件: ${outputPath}`)
  }
}

async function readLanguageData(
  i18nDir,
  languages,
  moduleFile,
  aliasConfig,
  isFile
) {
  const languageData = {}

  for (const lang of languages) {
    const langPath = join(i18nDir, lang)
    // 检查路径是否存在以及是文件还是目录
    let langFilePath
    if (isFile) {
      langFilePath = langPath + '.js'
    } else {
      // 如果路径不存在，默认当作目录处理
      langFilePath = join(langPath, moduleFile)
    }

    // 检查文件是否存在
    if (await fs.pathExists(langFilePath)) {
      try {
        // 预处理文件，解决路径别名问题
        const processedFilePath = await preprocessModuleFile(
          langFilePath,
          aliasConfig
        )

        // 动态导入模块
        const moduleUrl = pathToFileURL(processedFilePath).href
        const module = await import(moduleUrl)
        languageData[lang] = module.default || {}

        // 确保数据是普通对象
        if (
          typeof languageData[lang] !== 'object' ||
          languageData[lang] === null
        ) {
          console.warn(`语言数据不是对象类型: ${langFilePath}`)
          languageData[lang] = {}
        }

        // 如果创建了临时文件，清理它
        if (processedFilePath !== langFilePath) {
          await fs.remove(processedFilePath)
        }
      } catch (error) {
        console.warn(`导入模块失败 ${moduleFile}:`, error.message)
        console.error(error) // 输出完整错误信息用于调试
        languageData[lang] = {}

        // 确保清理临时文件
        try {
          const tempFilePath = langFilePath.replace(/\.js$/, '.tmp.js')
          if (await fs.pathExists(tempFilePath)) {
            await fs.remove(tempFilePath)
          }
        } catch (cleanupError) {
          // 忽略清理错误
        }
      }
    } else {
      console.log(`文件不存在: ${langFilePath}`)
      languageData[lang] = {}
    }
  }

  return languageData
}

// 预处理模块文件，解决路径别名问题
async function preprocessModuleFile(filePath, aliasConfig = { '@': './src' }) {
  const content = await fs.readFile(filePath, 'utf-8')

  // 检查是否包含任何配置的路径别名
  const hasAlias = Object.keys(aliasConfig).some((alias) =>
    content.includes(alias + '/')
  )
  if (!hasAlias) {
    // 如果不包含任何配置的路径别名，直接返回原文件路径
    // 处理省略.js后缀的导入路径
    return filePath
  }

  // 获取项目根目录
  const projectRoot = process.cwd()

  let processedContent = content

  // 处理每个配置的别名
  for (const [alias, aliasPath] of Object.entries(aliasConfig)) {
    // 解析别名路径为绝对路径
    const absoluteAliasPath = join(projectRoot, aliasPath)

    // 计算从当前文件到别名路径的相对路径
    const relativePathToAlias = relative(dirname(filePath), absoluteAliasPath)

    // 创建正则表达式匹配别名导入
    const aliasRegex = new RegExp(
      `(['"])${alias.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}\/([^'"]+)`,
      'g'
    )

    // 替换别名
    processedContent = processedContent.replace(
      aliasRegex,
      (match, quote, path) => {
        // 处理没有后缀的导入路径，尝试添加.js后缀
        let resolvedPath = `${relativePathToAlias}/${path}`
        // 如果路径不以.js结尾，尝试添加.js后缀
        if (!resolvedPath.endsWith('.js')) {
          resolvedPath = resolvedPath + '.js'
        }
        return `${quote}${resolvedPath}`
      }
    )
  }

  // 如果内容没有改变，直接返回原文件路径
  if (processedContent === content) {
    return filePath
  }

  // 创建临时文件，保持.js扩展名以便Node.js可以正确导入
  const tempFilePath = filePath.replace(/\.js$/, '.tmp.js')
  await fs.writeFile(tempFilePath, processedContent)

  return tempFilePath
}

async function getModuleFiles(i18nDir, searchLanguage, pattern) {
  // 简化实现，实际项目中可以使用glob等库
  const langPath = join(i18nDir, searchLanguage) // 以指定语言为准查找模块

  if (!(await fs.pathExists(langPath))) {
    // 如果路径不存在，尝试添加.js后缀再次检查
    const langPathWithExt = langPath + '.js'
    if (await fs.pathExists(langPathWithExt)) {
      // 如果是文件，直接返回文件名
      const relativePath = basename(langPathWithExt)
      return { files: [relativePath], isFile: true }
    }
    throw new Error(`语言文件或目录不存在: ${langPath}`)
  }

  // 如果是目录，支持多层目录结构
  const files = await getAllJsFiles(langPath, langPath)
  return { files, isFile: false }
}

// 递归获取所有JS文件，保持相对路径结构
async function getAllJsFiles(dir, baseDir) {
  const entries = await fs.readdir(dir)
  const files = []

  for (const entry of entries) {
    const fullPath = join(dir, entry)
    const stat = await fs.stat(fullPath)

    if (stat.isDirectory()) {
      // 递归处理子目录
      const subFiles = await getAllJsFiles(fullPath, baseDir)
      files.push(...subFiles)
    } else if (entry.endsWith('.js')) {
      // 计算相对于基础目录的路径
      const relativePath =
        dir === baseDir ? entry : join(dir.substring(baseDir.length + 1), entry)
      files.push(relativePath)
    }
  }

  return files
}

function readHistoryFileKey(historyFile) {
  const workbook = XLSX.readFile(historyFile)
  const historyKeys = new Set()
  for (const sheetName of workbook.SheetNames) {
    const worksheet = workbook.Sheets[sheetName]
    const data = XLSX.utils.sheet_to_json(worksheet)
    if(data.length > 0) {
      for(const item of data) {
        historyKeys.add(item.key)
      }
    }
  }
  return historyKeys;
}

async function createWorksheet(moduleFile, languageData, languages, config) {
  const { xlsxDir, historyFileName } = config
  const historyFile = join(xlsxDir, historyFileName)
  if (!(await fs.pathExists(historyFile))) {
    throw new Error(`history file 文件不存在: ${historyFile}`)
  }
  const historyDataKey = readHistoryFileKey(historyFile)

  // 收集所有键值对
  const allKeys = new Set()
  for (const lang in languageData) {
    collectKeys(languageData[lang], '', allKeys, historyDataKey)
  }

  console.log('keys:', allKeys.size)

  // 创建工作表数据
  const headers = ['key', ...languages]
  const rows = []

  for (const key of allKeys) {
    const row = [key]
    for (const lang of languages) {
      const value = getNestedValue(languageData[lang], key)
      row.push(value || '')
    }
    rows.push(row)
  }

  // 创建工作表
  const worksheetData = [headers, ...rows]
  const worksheet = XLSX.utils.aoa_to_sheet(worksheetData)

  return worksheet
}

// 递归收集对象的所有键（包括嵌套键）
function collectKeys(obj, prefix, keys, historyKeys) {
  for (const key in obj) {
    const fullKey = prefix ? `${prefix}.${key}` : key
    if (
      typeof obj[key] === 'object' &&
      obj[key] !== null &&
      !Array.isArray(obj[key])
    ) {
      collectKeys(obj[key], fullKey, keys)
    } else {
      if (historyKeys.has(fullKey)) {
        continue
      }
      keys.add(fullKey)
    }
  }
}

// 获取嵌套对象的值
function getNestedValue(obj, keyPath) {
  const keys = keyPath.split('.')
  let current = obj

  if (keyPath in current) {
    return current[keyPath]
  }

  for (const key of keys) {
    if (current && typeof current === 'object' && key in current) {
      current = current[key]
    } else {
      return undefined
    }
  }

  return current
}

async function generateFromFile(config) {
  const {
    i18nDir,
    xlsxDir,
    languages,
    xlsxFileName,
    searchLanguage = 'cn',
  } = config

  // 创建一个工作簿
  const workbook = XLSX.utils.book_new()

  console.log(`处理文件: ${searchLanguage}`)

  // 读取各语言的配置
  const languageData = await readLanguageData(
    i18nDir,
    languages,
    searchLanguage,
    config.alias,
    true
  )

  // 为当前模块创建一个工作表并添加到工作簿
  const worksheet = await createWorksheet(
    searchLanguage,
    languageData,
    languages,
    config
  )
  // 使用文件名作为工作表名称
  const sheetName = searchLanguage.substring(0, 31) // Excel工作表名称限制为31个字符
  XLSX.utils.book_append_sheet(workbook, worksheet, sheetName)

  // 写入文件，使用可配置的文件名
  const outputPath = join(xlsxDir, xlsxFileName)
  XLSX.writeFile(workbook, outputPath)

  console.log(`生成文件: ${outputPath}`)
}
